package com.ruoyi.rva.service.impl;

import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.rva.domain.*;
import com.ruoyi.rva.mapper.*;
import com.ruoyi.rva.service.IRvaMetaService;
import com.ruoyi.rva.service.IRvaViewbuttonService;
import com.ruoyi.rva.util.RvaConstants;
import com.ruoyi.rva.util.RvaJsonUtils;
import com.ruoyi.rva.util.RvaPinyinUtils;
import com.ruoyi.rva.util.RvaUtils;
import com.ruoyi.system.mapper.SysMenuMapper;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysDictTypeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * RVA元数据管理
 *
 * @author jiaodacailei
 * @date 2021-07-27
 */
@Service
@Transactional
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RvaMetaServiceImpl implements IRvaMetaService {

    private final RvaObjectMapper objectMapper;

    private final RvaPropertyMapper propertyMapper;

    private final RvaRelationMapper relationMapper;

    private final RvaRelationitemMapper relationitemMapper;

    private final RvaAppMapper appMapper;

    private final RvaAppitemMapper appitemMapper;

    private final RvaViewMapper viewMapper;

    private final RvaViewpropertyMapper viewpropertyMapper;

    private final RvaViewbuttonMapper viewbuttonMapper;

    private final RvaSchemaMapper schemaMapper;

    private final RvaDataMapper dataMapper;

    private final ISysDictTypeService dictTypeService;

    private final ISysDictDataService dictDataService;

    private final IRvaViewbuttonService viewbuttonService;

    @Override
    public void createObject(String table) {
        // 刪除元数据
        deleteObject(table);
        // 插入元数据
        RvaObject obj = new RvaObject();
        obj.setId(table);
        int idx = table.indexOf('_');
        obj.setNo(table.substring(idx + 1));
        String tableComment = schemaMapper.queryTableComment(table);
        obj.setName(RvaUtils.getString(tableComment, table));
        Long aLong = dataMapper.selectLong("select max(idx) from rva_object");
        obj.setIdx(aLong == null ? 0L : aLong + 1);
        obj.setStatus("0");
        obj.setModule(table.substring(0, idx));
        // 插入列元数据
        List<MySqlColumn> cols = schemaMapper.queryColumns(table);
        for (int i = 0; i < cols.size(); i ++) {
            MySqlColumn column = cols.get(i);
            if (column.isPrimaryKey()) {
                obj.addPropNameKey(column.getField());
                obj.setStatus("1");
            }
            RvaProperty property = createProperty(table, column, i);
            if (Arrays.asList("名称", "姓名", "标题", "name", "title").contains(property.getDescription())) {
                obj.setPropNameName(property.getName());
            }
        }
        if (RvaUtils.isEmpty(obj.getPropNameName())) {
            obj.setPropNameName(cols.get(1).getField());
        }
        obj.setPropIndexMax(cols.size());
        // 插入表元数据
        objectMapper.insertRvaObject(obj);
    }

    @Override
    public SysDictType createDictData(String table, String typeCnName, String defaultValue, String[] data, Boolean containValue) {
        String type = table.toLowerCase() + "_" + RvaPinyinUtils.getPinyinLower(typeCnName);
        RvaMap<String, Object> dict_type = new RvaMap<>("dict_type", type);
        dataMapper.deleteWhereMap("sys_dict_type", dict_type);
        dataMapper.deleteWhereMap("sys_dict_data", dict_type);
        SysDictType dictType = new SysDictType();
        dictType.setDictType(type);
        dictType.setDictName(typeCnName);
        dictType.setStatus("0");
        dictType.setCreateBy("admin");
        dictType.setCreateTime(new Date());
        dictTypeService.insertDictType(dictType);
        for (int i = 0; i < data.length; i += (containValue ? 2 : 1)) {
            String value = containValue ? data[i] : String.valueOf(RvaUtils.generateFixedLenNo(2, i + 1));
            String label = containValue ? data[i + 1] : data[i];
            SysDictData dictData = new SysDictData();
            dictData.setDictType(type);
            dictData.setDictLabel(label);
            dictData.setDictSort(Long.valueOf(i));
            dictData.setDictValue(value);
            dictData.setIsDefault("N");
            if (defaultValue != null && defaultValue.contains(value)) {
                dictData.setIsDefault("Y");
            }
            dictData.setCreateBy("admin");
            dictData.setCreateTime(new Date());
            dictDataService.insertDictData(dictData);
        }
        return dictType;
    }

    private RvaProperty createProperty (String table ,MySqlColumn col, Integer index) {
        RvaProperty property = new RvaProperty();
        property.setId(createPropertyId(table, col.getField()));
        property.setName(col.getField());
        property.setDescription(col.getDescription());
        property.setType(col.getLogicType());
        property.setTypeDetail(col.getTypeDetail());
        property.setRequired(col.isNull() ? "N" : "Y");
        property.setDefaultValue(col.getDefaultValue());
        property.setIdx(index + 1);
        property.setIdGenType(RvaUtils.isEmpty(col.getExtra()) ? "N" : col.getExtra().toUpperCase());
        property.setObjId(table);
        if (!RvaUtils.isEmpty(property.getDictType())) {
            String[] strings = property.getDictType().split("[ -]");
            SysDictType dictData = createDictData(table, property.getDescription(), property.getDefaultValue(), strings, property.getDictType().indexOf('-') > 0);
            property.setDictType(dictData.getDictType());
        }
        propertyMapper.insertRvaProperty(property);
        return property;
    }

    @Override
    public void deleteObject(String table) {
        // 刪除元数据
        dataMapper.delete("delete from rva_viewbutton where view_id in (select id from rva_view where obj_id = '" + table + "')");
        dataMapper.delete("delete from rva_viewproperty where view_id in (select id from rva_view where obj_id = '" + table + "')");
        dataMapper.deleteWhereMap("rva_view", new RvaMap<>("obj_id", table));
        dataMapper.delete("delete from rva_appitem where app_id in (select id from rva_app where obj_id = '" + table + "')");
        dataMapper.deleteWhereMap("rva_app", new RvaMap<>("obj_id", table));
        dataMapper.deleteWhereMap("rva_property", new RvaMap<>("obj_id", table));
        dataMapper.delete("delete from rva_relationitem where relation_id in (select id from rva_relation where obj_id = '" + table + "')");
        dataMapper.deleteWhereMap("rva_relation", new RvaMap<>("obj_id", table));
        dataMapper.deleteWhereMap("rva_object", new RvaMap<>("id", table));
    }

    private final static String DEFAULT_TAG = "x";

    private String createDefaultCrudId(String objId) {
        return createCrudId(DEFAULT_TAG, objId);
    }

    private String createAppId(String appType, String tag, String objId) {
        return String.format("%s%s_%s", appType, tag, objId);
    }

    private String createCrudId(String tag, String objId) {
        return String.format("%s%s_%s", RvaApp.TYPE_CRUD, tag, objId);
    }

    private String createViewId(String prefix, String tag, String objId) {
        return String.format("%s%s_%s", prefix, tag, objId);
    }

    private String createFormViewId(String tag, String objId, boolean isCreate) {
        String prefix = RvaView.FORM_UPDATE.substring(0, 1);
        if (isCreate) {
            prefix = RvaView.FORM_CREATE.substring(0, 1);
        }
        return createViewId(prefix, tag, objId);
    }

    private String createDefaultFormId(String objId, boolean isCreate) {
        return createFormViewId(DEFAULT_TAG, objId, isCreate);
    }

    private String createListViewId(String tag, String objId) {
        return createViewId(RvaView.TYPE_LIST.substring(0, 1), tag, objId);
    }

    private String createDefaultListViewId(String objId) {
        return createListViewId(DEFAULT_TAG, objId);
    }

    private String createSearchViewId(String tag, String objId) {
        return createViewId(RvaView.TYPE_SEARCH.substring(0, 1), tag, objId);
    }

    private String createDefaultSearchViewId(String objId) {
        return createSearchViewId(DEFAULT_TAG, objId);
    }

    private RvaView createDefaultListView(RvaObject obj) {
        RvaView list = new RvaView();
        list.setHeight(500);
        list.setId(createDefaultListViewId(obj.getId()));
        list.setIdx(0);
        list.setName(obj.getName() + "列表");
        list.setObjId(obj.getId());
        list.setWidth(720);
        list.setType(RvaView.TYPE_LIST);
        list.setListPaging("Y");
        list.setListRows(10);
        list.setLoadUrl(String.format("%s?searchId=%s", RvaView.URL_LOAD_LIST.replace("{view}", list.getId()), createDefaultSearchViewId(obj.getId())));
        viewMapper.insertRvaView(list);
        return list;
    }

    private RvaView createDefaultFormView(RvaObject obj, boolean isCreate) {
        RvaView form = new RvaView();
        form.setId(createDefaultFormId(obj.getId(), isCreate));
        form.setType(RvaView.TYPE_FORM);
        form.setIdx(0);
        String desc = "新建";
        if (!isCreate) {
            desc = "修改";
        }
        form.setName(desc + obj.getName());
        form.setObjId(obj.getId());
        form.setFormColumns(1);
        form.setWidth(900);
        form.setHeight(600);
        String url = isCreate ? RvaView.URL_LOAD_CREATE : RvaView.URL_LOAD_UPDATE;
        form.setLoadUrl(url.replace("{view}", form.getId()));
        // 设置修改视图加载时的where语句：表别名.主键字段 = '${列表视图中的主键属性ID}'
        form.setLoadWhereOfUpdateView(obj);
        viewMapper.insertRvaView(form);
        return form;
    }

    private RvaView createDefaultSearchView(RvaObject obj) {
        RvaView form = new RvaView();
        form.setId(createDefaultSearchViewId(obj.getId()));
        form.setIdx(0);
        form.setName("查询" + obj.getName());
        form.setType(RvaView.TYPE_SEARCH);
        form.setObjId(obj.getId());
        viewMapper.insertRvaView(form);
        return form;
    }

    private RvaViewproperty createDefaultListProp(RvaObject obj, RvaProperty prop, int i) {
        RvaViewproperty lp = new RvaViewproperty();
        String listViewId = createDefaultListViewId(obj.getId());
        lp.setId(createViewpropertyId(listViewId, prop.getName()));
        lp.setIdx(prop.getIdx());
        lp.setName(prop.getDescription());
        lp.setPropId(prop.getId());
        lp.setViewId(listViewId);
        if (i > 4) {
            lp.setType(RvaViewproperty.TYPE_HIDDEN);
        } else if (RvaUtils.isNotEmpty(prop.getDictType())) {
            lp.setType(RvaViewproperty.TYPE_DICTIONARY);
        } else {
            lp.setType(RvaViewproperty.TYPE_TEXT);
        }
        lp.setDictType(prop.getDictType());
        lp.setWidth(160);
        if (obj.isCreateTime(prop.getName())) {
            lp.setListOrderIdx(0);
            lp.setListOrderType(RvaViewproperty.LIST_ORDER_DESC);
        }
        viewpropertyMapper.insertRvaViewproperty(lp);
        return lp;
    }

    private RvaViewproperty createDefaultListButtonProp(RvaObject obj, int i) {
        RvaViewproperty lp = new RvaViewproperty();
        String listViewId = createDefaultListViewId(obj.getId());
        lp.setId(createViewpropertyId(listViewId, "operation"));
        lp.setIdx(i);
        lp.setName("操作");
        lp.setViewId(listViewId);
        lp.setType(RvaViewproperty.TYPE_BUTTON);
        // 设置
        // lp.setListButtons(RvaUtils.join(Arrays.asList(buttonIds), ","));
        viewpropertyMapper.insertRvaViewproperty(lp);
        return lp;
    }

    private RvaViewproperty createDefaultFormProp(RvaObject obj, RvaProperty prop, boolean isCreate) {
        RvaViewproperty fp = new RvaViewproperty();
        String formViewId = createDefaultFormId(obj.getId(), isCreate);
        fp.setId(createViewpropertyId(formViewId, prop.getName()));
        fp.setIdx(prop.getIdx());
        fp.setViewId(formViewId);
        fp.setName(prop.getDescription());
        fp.setPropId(prop.getId());
        this.setFormPropType(prop, fp);
        fp.setDictType(prop.getDictType());
        if (!RvaUtils.isEmpty(prop.getDictType())) {
            fp.setType(RvaViewproperty.TYPE_SELECT);
            fp.setFormSelectorSingle(prop.getDictSelectSingle());
        }
        fp.setFormRequired(prop.getRequired());
        fp.setFormColSpan(1);
        fp.setFormRowSpan(1);
        fp.setFormSubmit("Y");
        if (isCreate) {
            fp.setFormInitReplaceEmpty("Y");
            if (obj.isCreateBy(prop.getName()) || obj.isUpdateBy(prop.getName())) {// 创建人/修改人
                fp.setFormInitValue(RvaConstants.EXPRESSION_LOGIN_USER);
                fp.setType(RvaViewproperty.TYPE_HIDDEN);
            } else if (obj.isCreateTime(prop.getName()) || obj.isUpdateTime(prop.getName())) {// 创建时间/修改时间
                fp.setFormInitValue(RvaConstants.EXPRESSION_NOW);
                fp.setType(RvaViewproperty.TYPE_HIDDEN);
            }
        } else {
            if (obj.isCreateBy(prop.getName())) {// 创建人
                fp.setFormReadonly("Y");
            } else if (obj.isUpdateBy(prop.getName())) {// 修改人
                fp.setFormSubmitValue(RvaConstants.EXPRESSION_LOGIN_USER);
                fp.setFormSubmitReplace("Y");// 有值时替换为上述表达式的值
                fp.setFormSubmitReplaceEmpty("Y");// 无值时替换为上述表达式的值
                fp.setFormReadonly("Y");
            } else if (obj.isCreateTime(prop.getName())) {// 创建时间
                fp.setFormReadonly("Y");
            } else if (obj.isUpdateTime(prop.getName())) {// 修改时间
                fp.setFormSubmitValue(RvaConstants.EXPRESSION_NOW);
                fp.setFormSubmitReplace("Y");// 有值时替换为上述表达式的值
                fp.setFormSubmitReplaceEmpty("Y");// 无值时替换为上述表达式的值
                fp.setFormReadonly("Y");
            }
        }
        if (!RvaUtils.isEmpty(prop.getDefaultValue())) {
            fp.setFormInitValue(prop.getDefaultValue());
        }
        fp.setFormReadonly((obj.isPrimaryKey(prop.getName()) && !isCreate) ? "Y" : "N");
        viewpropertyMapper.insertRvaViewproperty(fp);
        return fp;
    }

    private void setFormPropType(RvaProperty prop, RvaViewproperty fp) {
        fp.setType(RvaViewproperty.TYPE_TEXT);
        switch (prop.getType()) {
            case RvaProperty.TYPE_DATE:
                fp.setType(RvaViewproperty.TYPE_DATE);
                break;
            case RvaProperty.TYPE_DATETIME:
                fp.setType(RvaViewproperty.TYPE_DATETIME);
                break;
            case RvaProperty.TYPE_INTEGER:
            case RvaProperty.TYPE_SMALLINT:
                fp.setType(RvaViewproperty.TYPE_NUMBER);
                Double max = Math.pow(10, RvaUtils.getInt(prop.getValueMax(), 10)) - 1;
                Double min = 0.0;
                fp.setFormValueMax(String.valueOf(max.intValue()));
                fp.setFormValueMin(String.valueOf(min.intValue()));
                // 设置数值控件允许的小数位数
                fp.setNumberScale(0);
                break;
            case RvaProperty.TYPE_NUMERIC:
                fp.setType(RvaViewproperty.TYPE_NUMBER);
                max = Math.pow(10, RvaUtils.getInt(prop.getValueMax(), 20)) - 1;
                min = 0.0;
                fp.setFormValueMax(max.toString());
                fp.setFormValueMin(min.toString());
                // 设置数值控件允许的小数位数
                fp.setNumberScale(prop.getNumberScale());
                break;
            case RvaProperty.TYPE_VARCHAR:
                fp.setFormValueMin("0");
                fp.setFormValueMax(prop.getValueMax());
                Integer length = RvaUtils.getInt(prop.getValueMax(), 0);
                if (length < 200) {
                    fp.setType(RvaViewproperty.TYPE_TEXT);
                } else if (length < 500) {
                    fp.setType(RvaViewproperty.TYPE_TEXTAREA);
                    fp.setHeight(100);
                } else {
                    fp.setType(RvaViewproperty.TYPE_TEXTAREA);
                    fp.setHeight(200);
                }
                break;
            case RvaProperty.TYPE_TEXT:
                fp.setType(RvaViewproperty.TYPE_TEXTAREA);
                fp.setHeight(200);
                break;
        }
    }

    /**
     * 创建nameProperty对应的查询字段
     * @param obj
     * @return
     */
    private RvaViewproperty createDefaultSearchProp(RvaObject obj) {
        RvaProperty prop = obj.getNameProperty();
        if (prop == null) {
            return null;
        }
        RvaViewproperty sp = new RvaViewproperty();
        String searchViewId = createDefaultSearchViewId(obj.getId());
        sp.setId(createViewpropertyId(searchViewId, prop.getName()));
        sp.setIdx(0);
        sp.setName(prop.getDescription());
        sp.setPropId(prop.getId());
        sp.setType(RvaViewproperty.TYPE_TEXT);
        sp.setSearchType(RvaViewproperty.SEARCH_LIKE);
        sp.setWidth(200);
        sp.setViewId(searchViewId);
        viewpropertyMapper.insertRvaViewproperty(sp);
        return sp;
    }

    /**
     * 列表视图新建按钮
     *
     * @param listId
     * @return
     */
    private RvaViewbutton createDefaultListCreateButton(String listId, String createId) {
        return viewbuttonService.createListCreateButton(RvaView.FORM_CREATE, 0, listId, createId, false);
    }

    /**
     * 列表视图修改按钮
     *
     * @param listId
     * @return
     */
    private RvaViewbutton createDefaultListUpdateButton(String listId, String updateId) {
        return viewbuttonService.createListUpdateButton(RvaView.FORM_UPDATE, 0, listId, updateId, false);
    }

    /**
     * 列表视图删除按钮
     *
     * @param listId
     * @return
     */
    private RvaViewbutton createDefaultListDeleteButton(String listId, String position) {
        String idSuffix = "delete_" + position;
        if (RvaViewbutton.POSITION_INNER.equals(position)) {
            return viewbuttonService.createListInnerDeleteButton(idSuffix, 3, listId);
        }
        return viewbuttonService.createListTopDeleteButton(idSuffix, 1, listId);
    }

    /**
     * 表单视图提交按钮
     *
     * @param formId 表单视图ID
     * @return
     */
    private RvaViewbutton createDefaultFormSubmitButton(String formId, boolean create) {
        String type = "submit_" + RvaView.FORM_UPDATE;
        if (create) {
            type = "submit_" + RvaView.FORM_CREATE;
            return viewbuttonService.createFormSubmitCreateButton(type, 0, formId);
        }
        return viewbuttonService.createFormSubmitUpdateButton(type, 0, formId);
    }

    /**
     * 表单视图重置按钮
     *
     * @param formId 表单视图ID
     * @return
     */
    private RvaViewbutton createDefaultFormResetButton(String formId, Boolean isCreate) {
        String idSuffix = "reset_" + getFormType(isCreate);
        return viewbuttonService.createFormResetButton(idSuffix, 1, formId);
    }

    private static String getFormType(Boolean isCreate) {
        return isCreate ? RvaView.FORM_CREATE : RvaView.FORM_UPDATE;
    }

    /**
     * 表单视图取消按钮
     *
     * @param formId 表单视图ID
     * @return
     */
    private RvaViewbutton createDefaultFormCancelButton(String formId, Boolean isCreate) {
        String idSuffix = "close_" + getFormType(isCreate);
        return viewbuttonService.createFormCancelButton(idSuffix, 2, formId);
    }

    private RvaApp createDefaultCrud(RvaObject obj) {
        RvaApp crud = new RvaApp();
        crud.setId(createDefaultCrudId(obj.getId()));
        crud.setIdx(0);
        crud.setName(obj.getName());
        crud.setObjId(obj.getId());
        Map map = new RvaMap<>(RvaView.FORM_CREATE, createDefaultFormId(obj.getId(), true))
                .rvaPut(RvaView.FORM_UPDATE, createDefaultFormId(obj.getId(), false))
                .rvaPut(RvaView.TYPE_LIST, createDefaultListViewId(obj.getId()))
                .rvaPut(RvaView.TYPE_SEARCH, createDefaultSearchViewId(obj.getId()));
        crud.setStatus("2");
        crud.setTemplate("rva/app/" + RvaApp.TYPE_CRUD);
        crud.setType(RvaApp.TYPE_CRUD);
        appMapper.insertRvaApp(crud);
        String[] types = {RvaView.FORM_CREATE, RvaView.FORM_UPDATE, RvaView.TYPE_LIST, RvaView.TYPE_SEARCH};
        String[] viewIds = {createDefaultFormId(obj.getId(), true), createDefaultFormId(obj.getId(), false),
                createDefaultListViewId(obj.getId()), createDefaultSearchViewId(obj.getId())};
        for (int i = 0; i < types.length; i++) {
            createCrudAppItem(crud.getId(), types[i], i, viewIds[i]);
        }
        return crud;
    }

    private void createCrudAppItem(String appId, String type, int index, String viewId) {
        RvaAppitem appitem = new RvaAppitem();
        appitem.setAppId(appId);
        appitem.setId(appId + "_" + type);
        appitem.setIdx(index);
        RvaView view = viewMapper.selectRvaViewById(viewId);
        appitem.setName(view.getName());
        appitem.setType(type);
        appitem.setRelatedAppId(viewId);
        appitem.setRelatedAppType(view.getType());
        appitemMapper.insertRvaAppitem(appitem);
    }

    @Override
    public void createCrud(String table) {
        // 1.获取RvaObject元数据（包含属性和关系）
        RvaObject obj = objectMapper.selectRvaObjectById(table);
        // 2.删除默认crud相关数据
        String defaultCrudId = createDefaultCrudId(table);
        deleteApp(defaultCrudId);
        // 3.创建默认crud相关数据
        // 创建列表视图
        RvaView list = this.createDefaultListView(obj);
        // 创建“新建”表单视图
        RvaView create = this.createDefaultFormView(obj, true);
        // 创建“修改”表单视图
        RvaView update = this.createDefaultFormView(obj, false);
        // 创建查询视图
        RvaView search = this.createDefaultSearchView(obj);
        // 根据属性创建各个视图属性
        List<RvaProperty> props = obj.getProperties();
        for (int i = 0; i < props.size(); i++) {
            RvaProperty prop = props.get(i);
            // 如果不是主键，并且是外键，则略过
            this.createDefaultListProp(obj, prop, i);
            this.createDefaultFormProp(obj, prop, true);
            this.createDefaultFormProp(obj, prop, false);
        }
        this.createDefaultSearchProp(obj);
        // 创建列表视图按钮
        RvaViewbutton createButton = createDefaultListCreateButton(list.getId(), create.getId());
        createDefaultListDeleteButton(list.getId(), RvaViewbutton.POSITION_TOP);
        RvaViewbutton updateButton = createDefaultListUpdateButton(list.getId(), update.getId());
        RvaViewbutton deleteButton = createDefaultListDeleteButton(list.getId(), RvaViewbutton.POSITION_INNER);
        // 创建列表操作列
        createDefaultListButtonProp(obj, 999);
        // 创建“新建”表单视图按钮
        createDefaultFormSubmitButton(create.getId(), true);
        createDefaultFormResetButton(create.getId(), true);
        createDefaultFormCancelButton(create.getId(), true);
        // 创建“修改”表单视图按钮
        createDefaultFormSubmitButton(update.getId(), false);
        createDefaultFormResetButton(update.getId(), false);
        createDefaultFormCancelButton(update.getId(), false);
        // 更新排序
        updateOrder(obj, list);
        // 创建crud
        createDefaultCrud(obj);
    }

    private void updateOrder (RvaObject obj, RvaView list) {
        if (obj.hasNotPropIndex()) {
            return;
        }
        list.getProperties().forEach(p -> {
            if (p.getListOrderIdx() >= 0) {
                p.setListOrderIdx(-1);
                viewpropertyMapper.updateRvaViewproperty(p);
            }
        });
        List<String> nameIndexWhere = obj.getPropNameIndexWhere();
        for (int i = 0; i < nameIndexWhere.size(); i++) {
            String propName = nameIndexWhere.get(i);
            RvaViewproperty p = list.getPropertyByObjectProperty(obj.getId() + "_" + propName);
            p.setListOrderIdx(i);
            p.setListOrderType(RvaViewproperty.LIST_ORDER_ASC);
            viewpropertyMapper.updateRvaViewproperty(p);
        }
        RvaViewproperty p = list.getPropertyByObjectProperty(obj.getId() + "_" + obj.getPropNameIndex());
        p.setListOrderIdx(nameIndexWhere.size());
        p.setListOrderType(RvaViewproperty.LIST_ORDER_ASC);
        viewpropertyMapper.updateRvaViewproperty(p);
    }

    private static String createId (String prefix, int num, String suffix) {
        return prefix + num + "_" + suffix;
    }

    public RvaApp cloneCrud(String appId, Boolean cascaded) {
        RvaApp crud = cloneById(appId, cascaded, true);
        for (int i = 0; i < crud.getAppItems().size(); i++) {
            RvaAppitem appitem = crud.getAppItems().get(i);
            String view = cloneViewInApp(appitem.getRelatedAppId());
            createCrudAppItem(crud.getId(), appitem.getType(), i, view);
        }
        // 更新update.loadWhere
        RvaApp oldCrud = appMapper.selectRvaAppById(appId);
        RvaView oldList = viewMapper.selectRvaViewById(oldCrud.getListId());
        RvaApp newCrud = appMapper.selectRvaAppById(crud.getId());
        RvaView newList = viewMapper.selectRvaViewById(newCrud.getListId());
        RvaView newUpdate = viewMapper.selectRvaViewById(newCrud.getUpdateId());
        newUpdate.setLoadWhere(newUpdate.getLoadWhere().replace(oldList.getId(), newList.getId()));
        viewMapper.updateRvaView(newUpdate);
        return newCrud;
    }

    private RvaApp cloneById (String appId, Boolean cascaded, Boolean cascadeDelete) {
        RvaApp app = appMapper.selectRvaAppById(appId).loadData();
        RvaApp search = new RvaApp();
        search.setObjId(app.getObjId());
        List<RvaApp> apps = appMapper.selectRvaAppList(search);
        // 获取RvaApp的ID中前缀的最大数字：crud6_rva_app，num=6
        int num = getMaxNumber(app.getType(), apps);
        String newId = createId (app.getType(), num, app.getObjId());
        app.setId(newId);
        app.setStatus("1");
        app.setIdx(num);
        app.setName(app.getName() + num);
        app.setViews(null);
        app.setApps(null);
        app.setCascadeDelete(cascadeDelete);
        app.setCascaded(cascaded);
        // CRUD
        appMapper.insertRvaApp(app);
        return app;
    }

    private String cloneViewInApp(String viewId) {
        RvaView view = cloneView(viewId, viewId.substring(0, 1), false);
        return view.getId();
    }

    private String copyViewId (String srcViewId, String prefix) {
        RvaView srcView = viewMapper.selectRvaViewById(srcViewId);
        return createViewId(srcView.getObjId(), prefix);
    }

    public String createViewId (String objId, String prefix) {
        RvaView search = new RvaView();
        search.setObjId(objId);
        List<RvaView> views = viewMapper.selectRvaViewList(search);
        int num = getMaxNumber(prefix, views);
        String viewId = prefix + num + "_" + objId;
        return viewId;
    }

    @Override
    public RvaView cloneSingleView(String srcViewId, Boolean cascaded) {
        return this.cloneView(srcViewId, srcViewId.substring(0, 1) + "c", cascaded);
    }

    @Override
    public RvaView cloneView(String srcViewId, String prefix, Boolean cascaded) {
        String newViewId = copyViewId(srcViewId, prefix);
        RvaView srcView = viewMapper.selectRvaViewById(srcViewId);
        RvaView newView = RvaUtils.cloneBySetter(srcView, RvaView.class);
        // 列表属性
        List<RvaViewproperty> props = newView.getProperties();
        for (RvaViewproperty prop : props) {
            prop.setId(this.copyViewPropertyOrButtonId(prop.getId(), newView.getId(), newViewId));
            if (RvaViewproperty.TYPE_BUTTON.equals(prop.getType()) && RvaUtils.isNotEmpty(prop.getListButtons())) {
                String buttonIds = this.copyViewPropertyOrButtonId(prop.getListButtons(), newView.getId(), newViewId);
                prop.setListButtons(buttonIds);
            }
            prop.setViewId(newViewId);
            viewpropertyMapper.insertRvaViewproperty(prop);
        }
        // 列表按钮
        List<RvaViewbutton> btns = newView.getButtons();
        for (RvaViewbutton btn : btns) {
            btn.setId(this.copyViewPropertyOrButtonId(btn.getId(), newView.getId(), newViewId));
            btn.setViewId(newViewId);
            if (RvaUtils.isNotEmpty(btn.getActionUrl())) {
                btn.setActionUrl(btn.getActionUrl().replace(newView.getId(), newViewId));
            }
            // 处理ActionDialogAppId，根据button.data.cascadeDelete来决定是否克隆应用
            if (RvaUtils.isNotEmpty(btn.getActionDialogAppId())) {
                if (btn.isCascadeDelete()) {
                    RvaApp cloneApp = cloneApp(btn.getActionDialogAppId(), true, true);
                    btn.setActionDialogAppId(cloneApp.getId());
                }
            }
            if (RvaUtils.isNotEmpty(btn.getActionDialogViewId())) {
                if (btn.isCascadeDelete()) {
                    RvaView cloneSingleView = this.cloneSingleView(btn.getActionDialogViewId(), true);
                    btn.setActionDialogViewId(cloneSingleView.getId());
                } else {
                    btn.setActionDialogViewId(btn.getActionDialogViewId().substring(0, 1) + newViewId.substring(1));
                }
            }
            viewbuttonMapper.insertRvaViewbutton(btn);
        }
        newView.setId(newViewId);
        if (RvaUtils.isNotEmpty(newView.getLoadUrl())) {
            newView.setLoadUrl(newView.getLoadUrl().replace(srcView.getId(), newView.getId()));
            if (newView.getType().equals(RvaView.TYPE_LIST)) {
                newView.setLoadUrl(newView.getLoadUrl().replace("s" + srcView.getId().substring(1), "s" + newView.getId().substring(1)));
            }
        }
        newView.setCascaded(cascaded);
        viewMapper.insertRvaView(newView);
        return newView;
    }

    private String copyViewPropertyOrButtonId(String id, String oldView, String newView) {
        return id.replace(oldView, newView);
    }

    private static int getMaxNumber(String prefix, List list) {
        int num = -1;
        for (Object o : list) {
            try {
                String id = (String) o.getClass().getMethod("getId").invoke(o);
                String[] strs = id.split("_");
                if (strs[0].startsWith(prefix) && strs[0].length() > prefix.length()) {
                    int n = RvaUtils.getInt(strs[0].substring(prefix.length()), num);
                    if (n > num) {
                        num = n;
                    }
                }
            } catch (Exception e) {
                log.warn(e.getMessage());
            }
        }
        num++;
        return num;
    }

    @Override
    public void deleteApp(String appId) {
        if (RvaUtils.isEmpty(appId)) {
            return;
        }
        RvaApp app = appMapper.selectRvaAppById(appId);
        if (app == null) {
            return;
        }
        app.getAppItems().forEach(rvaAppitem -> {
            if (app.isCascadeDelete()) {
                if (rvaAppitem.isView()) {
                    deleteView(rvaAppitem.getRelatedAppId());
                }
                if (rvaAppitem.isApp()) {
                    deleteApp(rvaAppitem.getRelatedAppId());
                }
            }
            appitemMapper.deleteRvaAppitemById(rvaAppitem.getId());
        });
        appMapper.deleteRvaAppById(appId);
    }

    public void deleteView(String viewId) {
        if (RvaUtils.isEmpty(viewId)) {
            return;
        }
        RvaView view = viewMapper.selectRvaViewById(viewId);
        if (view == null) {
            return;
        }
        view.getProperties().forEach(p -> {
            deleteViewpropertyCascaded(p);
        });
        view.getButtons().forEach(b -> {
            deleteViewbuttonCascaded(b);
        });
        dataMapper.delete("delete from rva_viewbutton where view_id = '" + viewId + "'");
        dataMapper.delete("delete from rva_viewproperty where view_id = '" + viewId + "'");
        viewMapper.deleteRvaViewById(viewId);
    }

    /**
     * 删除视图属性级联的数据
     * @param p
     */
    private void deleteViewpropertyCascaded (RvaViewproperty p) {
        if (p.isCascadeDelete()) {
            this.deleteApp(p.getFormRelatedCrud());
        }
    }

    /**
     * 删除视图按钮级联的数据
     * @param b
     */
    private void deleteViewbuttonCascaded(RvaViewbutton b) {
        if (b.isCascadeDelete()) {
            this.deleteApp(b.getActionDialogAppId());
            this.deleteView(b.getActionDialogViewId());
        }
    }

    @Override
    public void deleteViewbutton(String buttonId) {
        RvaViewbutton viewbutton = viewbuttonMapper.selectRvaViewbuttonById(buttonId);
        deleteViewbuttonCascaded(viewbutton);
        viewbuttonMapper.deleteRvaViewbuttonById(buttonId);
    }

    @Override
    public void deleteViewproperty(String viewpropertyId) {
        RvaViewproperty p = viewpropertyMapper.selectRvaViewpropertyById(viewpropertyId);
        deleteViewpropertyCascaded(p);
        viewpropertyMapper.deleteRvaViewpropertyById(viewpropertyId);
    }

    private final SysMenuMapper menuMapper;

    @Override
    public void createMenuAndButtons(String appId, Long parentId, Integer orderNum) {
        createMenuAndButtons(appId, parentId, orderNum, false);
    }

    private void createMenuAndButtons(String appId, Long parentId, Integer orderNum, Boolean btn) {
        RvaApp app = appMapper.selectRvaAppById(appId);
        SysMenu menu = new SysMenu();
        if (!btn) {
            dataMapper.deleteWhereMap("sys_menu", new RvaMap<>("path", appId));
            menu.setIcon("build");
            menu.setMenuName(app.getName());
            menu.setMenuType("C");
            menu.setCreateBy("admin");
            menu.setComponent(app.getTemplate());
            menu.setIsCache("0");
            menu.setIsFrame("1");
            menu.setOrderNum(RvaUtils.generateFixedLenNo(4, orderNum));
            menu.setPerms(appId);
            menu.setParentId(parentId);
            menu.setPath(appId);
            menu.setStatus("0");
            menu.setVisible("0");
            menu.setCreateTime(new Date());
            menu.setRemark(app.getName() + "菜单");
            menuMapper.insertMenu(menu);
        } else {
            menu = createButtonMenu(app.getId(), orderNum, app.getName(), parentId);
        }
        // 按钮
        for (int i = 0; i < app.getAppItems().size(); i++) {
            RvaAppitem appitem = app.getAppItems().get(i);
            if (RvaUtils.parseMap(appitem.getData()).rvaEquals("isPermission", "N")) {
                continue;
            }
            if (appitem.isView()) {
                RvaView v = viewMapper.selectRvaViewById(appitem.getRelatedAppId());
                SysMenu vMenu = createButtonMenu(v.getId(), i, v.getName(), menu.getMenuId());
                createButtonsMenu(vMenu.getMenuId(), v);
            }
            if (appitem.isApp()) {
                createMenuAndButtons(appitem.getRelatedAppId(), menu.getMenuId(), i, true);
            }
        }
    }

    private SysMenu createButtonMenu (String id, int index, String name, Long parentId) {
        dataMapper.deleteWhereMap("sys_menu", new RvaMap<String, Object>("perms", id).rvaPut("menu_type", "F"));
        SysMenu btnMenu = new SysMenu();
        btnMenu.setIcon("#");
        btnMenu.setMenuName(name);
        btnMenu.setMenuType("F");
        btnMenu.setCreateBy("admin");
        btnMenu.setIsCache("0");
        btnMenu.setIsFrame("1");
        btnMenu.setOrderNum(RvaUtils.generateFixedLenNo(4, index));
        btnMenu.setPerms(id);
        btnMenu.setParentId(parentId);
        btnMenu.setStatus("0");
        btnMenu.setVisible("0");
        btnMenu.setCreateTime(new Date());
        btnMenu.setRemark(name + "按钮");
        menuMapper.insertMenu(btnMenu);
        return btnMenu;
    }

    private void createButtonsMenu(Long parentId, RvaView v) {
        v.getButtons().forEach(btn -> {
            createButtonMenu(btn.getId(), btn.getIdx(), v.getName() + "-" + btn.getName(), parentId);
        });
    }

    @Override
    public void deleteMenuAndButtons(String... appIds) {
        for (String appId : appIds) {
            Long menuId = dataMapper.selectLong("select menu_id from sys_menu where path = '" + appId + "'");
            deleteMenuAndChildren(menuId);
        }
    }

    private void deleteMenuAndChildren(Long menuId) {
        List<RvaMap<String, Object>> list = dataMapper.selectList(String.format("select menu_id from sys_menu where parent_id = %d", menuId));
        list.forEach(m -> {
            deleteMenuAndChildren(m.getLong("menu_id"));
        });
        dataMapper.deleteWhereMap("sys_menu", new RvaMap<>("menu_id", menuId));
    }

    @Override
    public void createRvaMenu() {
        dataMapper.deleteWhereMap("sys_menu", new RvaMap<>("path", "rva"));
        SysMenu menu = new SysMenu();
        menu.setIcon("tool");
        menu.setMenuName("RVA管理");
        menu.setMenuType("M");
        menu.setCreateBy("admin");
        menu.setIsCache("0");
        menu.setIsFrame("1");
        menu.setOrderNum("5");
        menu.setParentId(0L);
        menu.setPath("rva");
        menu.setStatus("0");
        menu.setVisible("0");
        menu.setCreateTime(new Date());
        menu.setRemark("RVA管理目录");
        menuMapper.insertMenu(menu);
        createMenuAndButtons("crud0_rva_module", menu.getMenuId(), 1);
        createMenuAndButtons("crud1_rva_object", menu.getMenuId(), 2);
        createMenuAndButtons("crud1_rva_app", menu.getMenuId(), 3);
        createMenuAndButtons("tcrud0_rva_view", menu.getMenuId(), 4);
    }

    @Override
    public RvaApp createTreeCrud (List<String> contents, List<String> navs) {
        RvaApp tcrud = createTcrud(contents);
        addNavsToTreeCrud(navs, tcrud);
        addContentsToTreeCrud(contents, tcrud);
        return tcrud;
    }

    @Override
    public void addContentsToTreeCrud(List<String> contents, String appId) {
        RvaApp app = appMapper.selectRvaAppById(appId);
        addContentsToTreeCrud(contents, app);
    }

    @Override
    public void addNavsToTreeCrud(List<String> navs, String appId) {
        RvaApp app = appMapper.selectRvaAppById(appId);
        addNavsToTreeCrud(navs, app);
    }

    private void addContentsToTreeCrud(List<String> contents, RvaApp tcrud) {
        List<RvaAppitem> contentItems = tcrud.getAppItemsByType("content");
        for (int i = 0; i < contents.size(); i++) {
            String contentId = contents.get(i);
            RvaAppitem appitem = new RvaAppitem();
            appitem.setAppId(tcrud.getId());
            appitem.setType("content");
            appitem.setId(createAppitemId(tcrud, i + contentItems.size(), appitem));
            appitem.setIdx(i + contentItems.size());
            // 设置关联数据
            appitem.setRelatedAppId(contentId);
            RvaApp contentApp = appMapper.selectRvaAppById(contentId);
            if (contentApp != null) {
                appitem.setName(contentApp.getName());
                appitem.setRelatedAppType(contentApp.getType());
                if (tcrud.isCascadeDelete()) {
                    RvaApp cloneApp = cloneApp(contentId, true, true);
                    appitem.setRelatedAppId(cloneApp.getId());
                }
            } else {
                RvaView contentView = viewMapper.selectRvaViewById(contentId);
                appitem.setName(contentView.getName());
                appitem.setRelatedAppType(contentView.getType());
                if (tcrud.isCascadeDelete()) {
                    RvaView view = cloneSingleView(contentId, true);
                    appitem.setRelatedAppId(view.getId());
                }
            }
            appitemMapper.insertRvaAppitem(appitem);
        }
    }

    private void addNavsToTreeCrud(List<String> navs, RvaApp tcrud) {
        List<RvaAppitem> navItems = tcrud.getAppItemsByType("nav");
        //
        String parentId = null;
        if (navItems.size() > 0) {
            parentId = navItems.get(navItems.size() - 1).getId();
        }
        for (int i = 0; i < navs.size(); i++) {
            String navId = navs.get(i);
            RvaAppitem appitem = new RvaAppitem();
            appitem.setAppId(tcrud.getId());
            appitem.setType("nav");
            appitem.setId(createAppitemId(tcrud, i + navItems.size(), appitem));
            appitem.setIdx(i + navItems.size());
            RvaApp navApp = appMapper.selectRvaAppById(navId);
            appitem.setName(navApp.getName());
            appitem.setSubType("list");
            appitem.setParentId(parentId);
            appitem.setRelatedAppId(navId);
            if (tcrud.isCascadeDelete()) {
                RvaApp cloneApp = cloneApp(navId, true, true);
                appitem.setRelatedAppId(cloneApp.getId());
            }
            appitem.setRelatedAppType(navApp.getType());
            appitemMapper.insertRvaAppitem(appitem);
            parentId = appitem.getId();
        }
    }

    private String createAppitemId(RvaApp tcrud, int i, RvaAppitem appitem) {
        return String.format("%s_%s_%d", tcrud.getId(), appitem.getType(), i) + RvaUtils.generateKey32(4);
    }

    private RvaApp createTcrud(List<String> contentIds) {
        RvaApp c = appMapper.selectRvaAppById(contentIds.get(0));
        RvaView v = viewMapper.selectRvaViewById(contentIds.get(0));
        String objId = c == null ? v.getObjId() : c.getObjId();
        RvaApp search = new RvaApp();
        search.setObjId(objId);
        search.setType(RvaApp.TYPE_TCRUD);
        List<RvaApp> apps = appMapper.selectRvaAppList(search);
        // 获取crud的ID中前缀的最大数字：crud6_mapp，num=6
        String prefix = RvaApp.TYPE_TCRUD;
        int num = getMaxNumber(prefix, apps);
        String tcrudId = prefix + num + "_" + objId;
        // tcrud
        RvaApp tcrud = new RvaApp();
        tcrud.setId(tcrudId);
        tcrud.setStatus("1");
        tcrud.setIdx(num);
        tcrud.setType(RvaApp.TYPE_TCRUD);
        tcrud.setTemplate("rva/app/" + RvaApp.TYPE_TCRUD);
        tcrud.setObjId(objId);
        tcrud.setName("导航" + objectMapper.selectRvaObjectById(objId).getName() + (num == 0 ? "" : (num + 1)));
        // tcrud.setApps(RvaJsonUtils.writeAsString(new RvaMap("tcrud", contentIds).rvaPut("treeCruds", treeCruds)));
        tcrud.setData(RvaJsonUtils.writeAsString(new RvaMap("urlLoadNode", RvaApp.getUrlTreeCrudLoadNode(tcrudId)).rvaPut("urlLoadContent", RvaApp.getUrlTreeCrudLoadContent(tcrudId))));
        tcrud.setCascadeDelete(true);
        appMapper.insertRvaApp(tcrud);
        return tcrud;
    }

    @Override
    public RvaApp cloneApp(String appId, Boolean cascade, Boolean cascadeDelete) {
        RvaApp app = appMapper.selectRvaAppById(appId);
        if (RvaApp.TYPE_CRUD.equals(app.getType())) {
            return cloneCrud(appId, cascade);
        }
        app = cloneById(appId, cascade, cascadeDelete);
        for (int i = 0; i < app.getAppItems().size(); i++) {
            RvaAppitem rvaAppitem = RvaJsonUtils.readAsTypeByData(app.getAppItems().get(i), RvaAppitem.class);
            rvaAppitem.setAppId(app.getId());
            if (RvaAppitem.APP_CRUD.equals(rvaAppitem.getRelatedAppType())) {
                RvaApp cloneCrud = cloneCrud(rvaAppitem.getRelatedAppId(), cascade);
                rvaAppitem.setRelatedAppId(cloneCrud.getId());
            } else if (rvaAppitem.isApp()) {
                RvaApp cloneApp = cloneApp(rvaAppitem.getRelatedAppId(), cascade, cascadeDelete);
                rvaAppitem.setRelatedAppId(cloneApp.getId());
            } else if (rvaAppitem.isView()) {
                RvaView cloneSingleView = cloneSingleView(rvaAppitem.getRelatedAppId(), cascade);
                rvaAppitem.setRelatedAppId(cloneSingleView.getId());
            }
            rvaAppitem.setId(createAppitemId(app, i, rvaAppitem));
            appitemMapper.insertRvaAppitem(rvaAppitem);
        }
        return app;
    }

    @Override
    public void deleteApp(List<String> appIds) {
        for (String appId : appIds) {
            deleteApp(appId);
        }
    }
}
